﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.DependencyInjection;

namespace StyleMVVM.Data
{
	public static class IReflectionServiceExtensions
	{
		public static string GetPropertyAccessPath<T, TProp>(this IReflectionService reflectionService,
		                                                     Expression<Func<T, TProp>> propertyFunc)
		{
			return GetPropertyAccessPath(propertyFunc);
		}

		public static string GetPropertyAccessPath<T, TProp>(Expression<Func<T, TProp>> propertyFunc)
		{
			if (propertyFunc == null)
			{
				throw new ArgumentNullException("propertyFunc");
			}

			MemberExpression memberExpr = null;

			if (propertyFunc.Body.NodeType == ExpressionType.Convert)
			{
				memberExpr =
					((UnaryExpression)propertyFunc.Body).Operand as MemberExpression;
			}
			else if (propertyFunc.Body.NodeType == ExpressionType.MemberAccess)
			{
				memberExpr = propertyFunc.Body as MemberExpression;
			}

			if (memberExpr == null)
			{
				throw new ArgumentException("propertyFunc");
			}

			StringBuilder builder = new StringBuilder();

			while (memberExpr != null)
			{
				if (builder.Length != 0)
				{
					builder.Insert(0, '.');
				}

				builder.Insert(0, memberExpr.Member.Name);

				memberExpr = memberExpr.Expression as MemberExpression;
			}

			return builder.ToString();
		}

		public static T GetPropertyValue<T>(this IReflectionService reflectionService,
		                                    object objectValue,
		                                    Expression<Func<T, object>> property,
		                                    object index = null,
		                                    bool throwIfMissing = false)
		{
			string propertyName = "";

			MemberExpression member = property.Body as MemberExpression;

			if (member == null)
			{
				throw new ArgumentException("property", "Property must be a property on type" + typeof(T).FullName);
			}

			while (member != null)
			{
				if (!string.IsNullOrEmpty(propertyName))
				{
					propertyName += ".";
				}

				propertyName += member.Member.Name;

				member = member.Expression as MemberExpression;
			}

			return (T)reflectionService.GetPropertyValue(objectValue, propertyName, index, throwIfMissing);
		}

		public static void SetPropertyValue<T>(this IReflectionService reflectionService,
		                                       object objectValue,
		                                       Expression<Func<T, object>> property,
		                                       T tValue,
		                                       object index = null,
		                                       bool throwIfMissing = false)
		{
			string propertyName = "";

			MemberExpression member = property.Body as MemberExpression;

			if (member == null)
			{
				throw new ArgumentException("property", "Property must be a property on type" + typeof(T).FullName);
			}

			while (member != null)
			{
				if (!string.IsNullOrEmpty(propertyName))
				{
					propertyName += ".";
				}

				propertyName += member.Member.Name;

				member = member.Expression as MemberExpression;
			}

			reflectionService.SetPropertyValue(objectValue, propertyName, tValue, index, throwIfMissing);
		}
	}
}